import axios, { AxiosResponse } from 'axios' 
import type { AxiosInstance, AxiosRequestConfig } from 'axios'
import type { RequestConfig, RequestInterceptors,CancelRequestSource} from './types'
import  message  from 'naive-ui'
import router from '../router/index' //userrouter的方式只能在setup中使用
/**
 * 通过create创建一个新实例
 * 对实例的配置，拦截做一个新的封装
 */
class Request {
  router:any
  instance: AxiosInstance //声明类型 其实就是一个axios实例 实例的接口已经在aixos中定义直接用即可
  interceptorsObj?: RequestInterceptors //自定义单独接口拦截器 
  cancelRequestSourceList?: CancelRequestSource[]
  requestUrlList?: string[]//字符串数组
  constructor(config: RequestConfig) {//config类型用已存在的RequestConfig接口进行限制
    this.instance = axios.create(config)
    this.requestUrlList = []
    this.cancelRequestSourceList = []
    this.router = router
    // this.interceptorsObj = config.interceptors //自定义拦截器，实现每个接口可以单独定义请求拦截器
    this.instance.interceptors.request.use(//interceptros本身已经存在axios中，现在用instance继承之后进行修改原来的interceptors
      (res: AxiosRequestConfig) => {
          res.headers!.Authorization = localStorage.getItem('token')
        return res
      },
      (err: any) => err,
    )//这里后面封装了多个请求拦截器，请求拦截器的执行顺序和注册顺序相反 执行顺序为 请求拦截==实例拦截==全局拦截
    this.instance.interceptors.response.use(
      (res: AxiosResponse) => { //全局响应拦截器 检测到token过期或者失效则重新返回登录
        if(res.data.status==401){
          this.router.push('/')
          window.$message.warning( 
            res.data.message
          )
          localStorage.setItem('token','')
        }
        return res.data 
      },
      (err: any) => err,
    )
  }
  /**
   * @description: 获取指定 url 在 cancelRequestSourceList 中的索引
   * @param {string} url
   * @returns {number} 索引位置
   */
  private getSourceIndex(url: string): number {
    return this.cancelRequestSourceList?.findIndex(
      (item: CancelRequestSource) => {
        return Object.keys(item)[0] === url
      },
    ) as number
  }
  /**
   * @description: 删除 requestUrlList 和 cancelRequestSourceList
   * @param {string} url
   * @returns {*}
   */
  private delUrl(url: string) {
    const urlIndex = this.requestUrlList?.findIndex(u => u === url)
    const sourceIndex = this.getSourceIndex(url)
    // 删除url和cancel方法
    urlIndex !== -1 && this.requestUrlList?.splice(urlIndex as number, 1)
    sourceIndex !== -1 &&
      this.cancelRequestSourceList?.splice(sourceIndex as number, 1)
  }

  request<T>(config: RequestConfig): Promise<T> {
    return new Promise((resolve, reject) => {
      // 如果单个请求有拦截器，拦截后重新给config赋值
      if (config.interceptors?.requestInterceptors) {
        config = config.interceptors.requestInterceptors(config)
      }
      const url = config.url
      config.headers = {
            'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8',
        }
      // url存在保存取消请求方法和当前请求url
      if (url) {
        if(this.requestUrlList.includes(url)){
          // this.cancelRequest(url)
          // console.log('重复请求')
        }else{
          this.requestUrlList?.push(url)
          config.cancelToken = new axios.CancelToken(c => {
            this.cancelRequestSourceList?.push({
              [url]: c,
            })
          })
        }
        
      }
      this.instance
        .request<any, T>(config)//真正调用实例的request
        .then(res => {
          // 如果我们为单个响应设置拦截器，这里使用单个响应的拦截器
          if (config.interceptors?.responseInterceptors) {
            res = config.interceptors.responseInterceptors<T>(res)
          }
          resolve(res)
        })
        .catch((err: any) => {
          reject(err)
        })
        .finally(() => {
          url && this.delUrl(url)
        })
    })
  }
  // 取消请求
  cancelRequest(url: string | string[]) {
    if (typeof url === 'string') {
      // 取消单个请求
      const sourceIndex = this.getSourceIndex(url)
      sourceIndex >= 0 && this.cancelRequestSourceList?.[sourceIndex][url]()
    } else {
      // 存在多个需要取消请求的地址
      url.forEach(u => {
        const sourceIndex = this.getSourceIndex(u)
        sourceIndex >= 0 && this.cancelRequestSourceList?.[sourceIndex][u]()
      })
    }
  }
  // 取消全部请求
  cancelAllRequest() {
    this.cancelRequestSourceList?.forEach(source => {
      const key = Object.keys(source)[0]
      source[key]()
    })
  }
}
export default Request

export { RequestConfig, RequestInterceptors }
